//////////////////////////////////////////////
// Node.h
//
//////////////////////////////////////////////

/// Defines / Macros -------------------------

#pragma once

/// Forward decl -----------------------------

namespace nkGraphics
{
	class Entity ;
	class NodeChangeListener ;
	class System ;
}

/// Includes ---------------------------------

// nkGraphics
#include "../Dll/DllDefines.h"

// nkExport
#include <NilkinsExport/Exportable/Exportable.h>

// nkMaths
#include <NilkinsMaths/Algebra/Vector.h>
#include <NilkinsMaths/Algebra/Quaternion.h>
#include <NilkinsMaths/Algebra/Matrix.h>

// nkMemory
#include <NilkinsMemory/Containers/String.h>

#include <NilkinsMemory/Pointers/UniquePtr.h>

// Standards
#include <vector>

/// Class ------------------------------------

namespace nkGraphics
{	
	class DLL_GRAPHICS_EXPORT Node : public nkExport::Exportable
	{
		public :

			// Destructor
			virtual ~Node () ;

			// Position
			virtual void setPositionRelative (const nkMaths::Vector& value) ;
			virtual void setPositionAbsolute (const nkMaths::Vector& value) ;
			virtual void translateRelative (const nkMaths::Vector& value) ;
			virtual void translateAbsolute (const nkMaths::Vector& value) ;
			nkMaths::Vector getPositionRelative () const ;
			nkMaths::Vector getPositionAbsolute () ;

			// Orientation
			virtual void setOrientationRelative (const nkMaths::Quaternion& value) ;
			virtual void setOrientationAbsolute (const nkMaths::Quaternion& value) ;
			virtual void rotateRelative (const nkMaths::Quaternion& value) ;
			virtual void rotateAbsolute (const nkMaths::Quaternion& value) ;
			nkMaths::Quaternion getOrientationRelative () const ;
			nkMaths::Quaternion getOrientationAbsolute () ;

			// Scale
			virtual void setScaleRelative (const nkMaths::Vector& value) ;
			virtual void setScaleAbsolute (const nkMaths::Vector& value) ;
			virtual void applyScale (const nkMaths::Vector& value) ;
			nkMaths::Vector getScaleRelative () const ;
			nkMaths::Vector getScaleAbsolute () ;

			// Transformation
			void setRelativeTransform (const nkMaths::Matrix& value) ;
			void setAbsoluteTransform (const nkMaths::Matrix& value) ;
			nkMaths::Matrix getRelativeTransform () ;
			nkMaths::Matrix getAbsoluteTransform () ;
			void updateTransformations () ;
			void dispatchDirtyTransform () ;

			// Axes
			nkMaths::Vector getAbsoluteUp () ;
			nkMaths::Vector getAbsoluteFront () ;
			nkMaths::Vector getAbsoluteRight () ;

			// Hierarchy
			void setParentNode (Node* parent) ;
			Node* getParentNode () const ;
			Node* getChildNode (unsigned int index) const ;
			unsigned int getNumChildNode () const ;

			// Name
			void setName (nkMemory::StringView value) ;
			nkMemory::StringView getName () const ;

			// Resource
			bool getHidden () const ;
			void setHidden (bool value) ;

			// Entity tracking
			void addEntityToTrack (Entity* entity) ;
			void stopEntityTracking (Entity* entity) ;
			unsigned int getTrackedEntityCount () const ;
			Entity* getTrackedEntity (unsigned int index) const ;

			// Listeners
			void registerListener (NodeChangeListener* listener) ;
			void unregisterListener (NodeChangeListener* listener) ;

			// Import / Export
			virtual void exportClassToTree (nkExport::Node* rootNode) override ;
			virtual void importClassFromTree (nkExport::Node* rootNode) override ;

		public :

			// Statics
			static nkMemory::UniquePtr<Node> create (System* system = nullptr) ;

		protected :

			// Functions
			// Constructors
			Node (System* system) noexcept ;

			// Manipulations without impacting other node
			void _forgetChildNode (Node* child) ;
			void _setParentNode (Node* parent) ;

		protected :
		
			// Attributes
			// Name for tracking
			nkMemory::String _name ;

			// Attached objects
			std::vector<Entity*> _entities ;

			// Informations spatiales
			nkMaths::Vector _position ;
			nkMaths::Quaternion _orientation ;
			nkMaths::Vector _scale ;

			// Cached transformations
			nkMaths::Matrix _relativeTransform ;
			nkMaths::Matrix _absoluteTransform ;

			// Les nodes fils et pères
			Node* _parentNode ;
			std::vector<Node*> _childrenNodes ;

			// Listeners
			std::vector<NodeChangeListener*> _changeListeners ;

			// System
			System* _system ;

			// Si transfo à update
			bool _transformDirty ;
			// Hidden from exporting
			bool _hidden ;
	} ;
}